package {{.pkg}}
{{if .withCache}}
import (
	"context"
	"database/sql"
	"errors"
	"github.com/huandu/go-sqlbuilder"
	"github.com/zeromicro/go-zero/core/mr"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"math/rand"
	"runtime"
)
{{else}}

import (
	"context"
	"database/sql"
	"errors"
	"github.com/huandu/go-sqlbuilder"
	"github.com/zeromicro/go-zero/core/mr"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"math/rand"
	"runtime"
    )
{{end}}
var _ {{.upperStartCamelObject}}Inter = (*custom{{.upperStartCamelObject}}Inter)(nil)

type (
	// {{.upperStartCamelObject}}Inter is an interface to be customized, add more methods here,
	// and implement the added methods in custom{{.upperStartCamelObject}}Inter.
	{{.upperStartCamelObject}}Inter interface {
		{{.lowerStartCamelObject}}Inter
		{{if not .withCache}}withSession(session sqlx.Session) {{.upperStartCamelObject}}Inter{{end}}
        // Transact 事务
        Transact(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error
		// UpdateWithVersion 按照版本号更新 单条
		UpdateWithVersion(ctx context.Context, session sqlx.Session, {{if .containsIndexCache}}newData{{else}}data{{end}} *{{.upperStartCamelObject}}) (int64, error)
		// PartialUpdate 按照条件更新某几项数据 单条
		PartialUpdate(ctx context.Context, session sqlx.Session, builder sqlbuilder.UpdateBuilder) (int64, error)
		// PartialRetrieve 按照条件查询单条记录
        PartialRetrieve(ctx context.Context, builder sqlbuilder.SelectBuilder) (*{{.upperStartCamelObject}}, error)
	    // PartialDelete 根据查询条件删除一条记录
    	PartialDelete(ctx context.Context, session sqlx.Session, builder sqlbuilder.DeleteBuilder) (int64, error)
		// Find 查询列表
		Find(ctx context.Context, builder sqlbuilder.SelectBuilder) ([]*{{.upperStartCamelObject}}, error)
		// Ids 查询id集合
		Ids(ctx context.Context, builder sqlbuilder.SelectBuilder) ([]any, error)
		// Count 统计查询个数
		Count(ctx context.Context, builder sqlbuilder.SelectBuilder, field string) (int64, error)
        // Sum 某个字段求和
        Sum(ctx context.Context, builder sqlbuilder.SelectBuilder, field string) (float64, error)
        // Exists 查询是否存在
        Exists(ctx context.Context, builder sqlbuilder.SelectBuilder) (bool, error)
		// PerformBulkCreate 批量创建
		PerformBulkCreate(ctx context.Context, session sqlx.Session, objs []*{{.upperStartCamelObject}}) map[any]error
        BulkCreate(ctx context.Context, session sqlx.Session, objs []*{{.upperStartCamelObject}}) error
        // PerformBulkUpdate 批量更新
        PerformBulkUpdate(ctx context.Context, session sqlx.Session, objs []*User) map[any]error
        BulkUpdate(ctx context.Context, session sqlx.Session, builder sqlbuilder.UpdateBuilder) error
        // PerformBulkDelete 批量删除
        PerformBulkDelete(ctx context.Context, session sqlx.Session, ids []any) map[any]error
        BulkDelete(ctx context.Context, session sqlx.Session, builder sqlbuilder.DeleteBuilder) error
        /*
            缓存部分
        */
        // PrimaryKey 获取主键key
        {{if  .withCache}}
        PrimaryKey(id any) string
        // SetCache 设置缓存
        SetCache(ctx context.Context, key string, value any, expire int64) error
        // GetCache 根据key获取缓存
        GetCache(ctx context.Context, key string, value any) error
        // DeleteCache 删除缓存
        DeleteCache(ctx context.Context, key string) error
        //DeleteCacheById 删除缓存根据id
        DeleteCacheById(ctx context.Context, id any) error

        {{end}}
	}

	custom{{.upperStartCamelObject}}Inter struct {
		*default{{.upperStartCamelObject}}Inter
	}
)

// New{{.upperStartCamelObject}}Inter returns a model for the database table.
func New{{.upperStartCamelObject}}Inter(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) {{.upperStartCamelObject}}Inter {
	return &custom{{.upperStartCamelObject}}Inter{
		default{{.upperStartCamelObject}}Inter: new{{.upperStartCamelObject}}Inter(conn{{if .withCache}}, c, opts...{{end}}),
	}
}

{{if not .withCache}}
func (m *custom{{.upperStartCamelObject}}Inter) withSession(session sqlx.Session) {{.upperStartCamelObject}}Inter {
    return New{{.upperStartCamelObject}}Inter(sqlx.NewSqlConnFromSession(session))
}

{{end}}

{{if .withCache}}
func (m *custom{{.upperStartCamelObject}}Inter) PrimaryKey(id any) string {
	{{if .withCache}}
	return m.formatPrimary(id)
	{{else}}
	return fmt.Sprintf("%s%v", fmt.Sprintf("cache:cms:%s:id:", m.table), id)
	{{end}}
}

func (m *custom{{.upperStartCamelObject}}Inter) SetCache(ctx context.Context, key string, value any, expire int64) error {
	return m.SetCacheWithExpireCtx(ctx, key, value, time.Duration(expire)*time.Minute)
}
func (m *custom{{.upperStartCamelObject}}Inter) GetCache(ctx context.Context, key string, value any) error {
	return m.GetCacheCtx(ctx, key, value)
}
func (m *custom{{.upperStartCamelObject}}Inter) DeleteCache(ctx context.Context,key string) error {
    return m.DelCacheCtx(ctx, key)
}
func (m *custom{{.upperStartCamelObject}}Inter) DeleteCacheById(ctx context.Context, id any) error {
	return m.DelCacheCtx(ctx, m.formatPrimary(id))
}


{{end}}

func (m *custom{{.upperStartCamelObject}}Inter) Transact(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
    {{if .withCache}}
   return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
        return fn(ctx, session)
    })
    {{else}}
   return m.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
        return fn(ctx, session)
    })
    {{end}}
}
func (m *customUserInter) createBuilder(build sqlbuilder.InsertBuilder) *sqlbuilder.InsertBuilder {
	return build.InsertInto(m.table)
}
func (m *custom{{.upperStartCamelObject}}Inter) updateBuilder(build sqlbuilder.UpdateBuilder) *sqlbuilder.UpdateBuilder {
	return build.Update(m.table)
}
func (m *custom{{.upperStartCamelObject}}Inter) selectBuilder(build sqlbuilder.SelectBuilder) *sqlbuilder.SelectBuilder {
    return build.Select("id").From(m.table)
}
func (m *custom{{.upperStartCamelObject}}Inter) deleteBuilder(build sqlbuilder.DeleteBuilder) *sqlbuilder.DeleteBuilder {
	return build.DeleteFrom(m.table)
}
func (m *custom{{.upperStartCamelObject}}Inter) countBuilder(builder sqlbuilder.SelectBuilder, field string) (string, []interface{}) {
	return builder.Select("COUNT(" + field + ")").Build()
}
func (m *custom{{.upperStartCamelObject}}Inter) sumBuilder(build sqlbuilder.SelectBuilder, field string) (string, []interface{}) {
	return build.Select("IFNULL(SUM(" + field + "),0)").Build()
}

func (m *custom{{.upperStartCamelObject}}Inter) PartialUpdate(ctx context.Context, session sqlx.Session, builder sqlbuilder.UpdateBuilder) (int64, error) {
    query, args := m.updateBuilder(builder).Build()
    query += "limit 1;"
    return m.exec(ctx,session,query,args)
}

func (m *custom{{.upperStartCamelObject}}Inter) PartialRetrieve(ctx context.Context, builder sqlbuilder.SelectBuilder) (*{{.upperStartCamelObject}}, error){
    query, args := m.selectBuilder(builder).Build()
    var id {{.dataType}}
    query += " limit 1;"

    {{if .withCache}} if err := m.QueryRowNoCacheCtx(ctx, &id, query, args...);err != nil{
        return nil, err
    }{{else}}
    if err := m.conn.QueryRowCtx(ctx, &id, query, args...);err != nil{
        return nil, err
    }  {{end}}
    return m.Retrieve(ctx,id)
}
func (m *custom{{.upperStartCamelObject}}Inter) PartialDelete(ctx context.Context, session sqlx.Session, builder sqlbuilder.DeleteBuilder) (int64, error) {
	query, args := m.deleteBuilder(builder).Build()
	query += " limit 1;"
    return m.exec(ctx,session,query, args)
}

func (m *custom{{.upperStartCamelObject}}Inter) Find(ctx context.Context, builder sqlbuilder.SelectBuilder) ([]*{{.upperStartCamelObject}}, error) {
    ids,err := m.Ids(ctx,builder)
    switch  {
    case err == nil:
        data, err := m.list(ctx, ids)
        return data.([]*{{.upperStartCamelObject}}), err
    case errors.Is(err, sqlx.ErrNotFound):
            return nil, ErrNotFound
    default:
        return nil, err
    }
}
func (m *custom{{.upperStartCamelObject}}Inter) Ids(ctx context.Context, builder sqlbuilder.SelectBuilder) ([]any, error) {
    var ids []{{.dataType}}
    query, values :=  m.selectBuilder(builder).Build()
    {{if .withCache}} if err := m.QueryRowsNoCacheCtx(ctx,&ids, query, values...);err != nil{
        return nil, err
    }{{else}}
    if err := m.conn.QueryRowsCtx(ctx,&ids, query, values...);err != nil{
        return nil, err
    }  {{end}}
    if ids == nil{
        return nil, ErrNotFound
    }
    var objs []any
    for _,id := range ids {
        objs = append(objs, id)
    }
    return objs,nil
}

func (m *custom{{.upperStartCamelObject}}Inter) Count(ctx context.Context, builder sqlbuilder.SelectBuilder, field string) (int64, error) {
    var count int64
    query, values := m.countBuilder(*builder.From(m.table), field)
    {{if .withCache}}err := m.QueryRowNoCacheCtx(ctx,&count, query, values...){{else}}
    err := m.conn.QueryRowCtx(ctx,&count, query, values...){{end}}
    switch err {
    case nil:
        return count, nil
    default:
        return 0, err
    }
}

func (m *custom{{.upperStartCamelObject}}Inter) Sum(ctx context.Context, builder sqlbuilder.SelectBuilder, field string) (float64, error){
    query, values := m.sumBuilder(*builder.From(m.table), field)
    var count float64
    {{if .withCache}}err := m.QueryRowNoCacheCtx(ctx,&count, query, values...){{else}}
    err := m.conn.QueryRowCtx(ctx,&count, query, values...){{end}}
    switch err {
    case nil:
        return count, nil
    default:
        return 0, err
    }
}
func (m *custom{{.upperStartCamelObject}}Inter) list(ctx context.Context, ids []any) (interface{}, error) {
	workers := m.workers(ids)
	return mr.MapReduce(func(source chan<- any) {
		for _, id := range ids {
			source <- id
		}
	}, func(item any, writer mr.Writer[any], cancel func(error)) {
		id := item.(any)
		one, err := m.Retrieve(ctx, id)
		if err == nil {
			writer.Write(one)
		} else {
			cancel(err)
		}
	}, func(pipe <-chan any, writer mr.Writer[any], cancel func(error)) {
		var entities []*{{.upperStartCamelObject}}
		for v := range pipe {
			a := v.(*{{.upperStartCamelObject}})
			entities = append(entities, a)
		}
		dict := m.sort(ids)
		data := make([]*{{.upperStartCamelObject}}, len(entities))
		for _, v := range entities {
			data[dict[v.Id]] = v
		}
		writer.Write(data)
	}, mr.WithWorkers(workers))
}

func (m *custom{{.upperStartCamelObject}}Inter) sort(ids []any) map[any]int {
	dict := map[any]int{}
	for k, v := range ids {
		dict[v] = k
	}
	return dict
}
func (m *custom{{.upperStartCamelObject}}Inter) workers(ids []any) int {
	return rand.Int()%50 + runtime.NumCPU()/10000
}

func (m *custom{{.upperStartCamelObject}}Inter) exec(ctx context.Context, session sqlx.Session, query string, args ...any) (int64, error) {
	var result sql.Result
    var err error
    {{if .withCache}}
        if session != nil{
            result,err =  session.ExecCtx(ctx,query, args)
        }else{
            result,err =  m.ExecNoCacheCtx(ctx, query, args)
        }
    {{else}}
        if session != nil{
            result,err = session.ExecCtx(ctx, query,args)
        }else{
            result,err = m.conn.ExecCtx(ctx,  query,args)
        }
    {{end}}
    if err != nil {
        return 0, err
    }
    affected, err := result.RowsAffected()
    if err != nil {
        return affected, err
    }
    if affected == 0 {
        return affected, NotModified
    }
    return affected, nil
}

func (m *custom{{.upperStartCamelObject}}Inter) Exists(ctx context.Context, builder sqlbuilder.SelectBuilder) (bool, error) {
	selectBuilder := sqlbuilder.NewSelectBuilder()
	selectBuilder.Select(selectBuilder.Exists(m.selectBuilder(builder)))
	query, args := selectBuilder.Build()
	affected,err := m.exec(ctx,nil,query,args)
	if err != nil  || affected == 0{
		return false, err
	}
	return true, nil
}

func (m *custom{{.upperStartCamelObject}}Inter) PerformBulkCreate(ctx context.Context,session sqlx.Session, objs []*{{.upperStartCamelObject}}) (errs map[any]error ){
	p := m.Goroutines(ctx, len(objs))
    for _, obj := range objs {
        obj := obj
        p.Go(func(ctx context.Context) error {
            if _, err := m.Create(ctx, session, obj); err != nil {
                errs[obj.Id] = err
            }
             //<-ctx.Done()
            return nil
        })
    }
    if err := p.Wait(); err != nil {
        errs[-1] = err
        return
    }
    return
}

func (m *custom{{.upperStartCamelObject}}Inter) PerformBulkUpdate(ctx context.Context,session sqlx.Session, objs []*{{.upperStartCamelObject}}) (errs map[any]error ){
	p := m.Goroutines(ctx, len(objs))
    for _, obj := range objs {
        obj := obj
        p.Go(func(ctx context.Context) error {
            if _, err := m.UpdateWithVersion(ctx, session, obj); err != nil {
                errs[obj.Id] = err
            }
             //<-ctx.Done()
            return nil
        })
    }
    if err := p.Wait(); err != nil {
        errs[-1] = err
        return
    }
    return
}

func (m *custom{{.upperStartCamelObject}}Inter) BulkUpdate(ctx context.Context, session sqlx.Session, builder sqlbuilder.UpdateBuilder) error {
	query, args := m.updateBuilder(builder).Build()
	_, err := m.exec(ctx, session, query, args)
	return err
}

func (m *custom{{.upperStartCamelObject}}Inter) PerformBulkDelete(ctx context.Context,session sqlx.Session, ids []any) (errs map[any]error ){
	p := m.Goroutines(ctx, len(ids))
    for _, id := range ids {
        id := id
        p.Go(func(ctx context.Context) error {
            if _, err := m.Delete(ctx, session, id); err != nil {
                errs[id] = err
            }
            //<-ctx.Done()
            return nil
        })
    }
    if err := p.Wait(); err != nil {
        errs[-1] = err
        return
    }
    return
}
func (m *custom{{.upperStartCamelObject}}Inter)BulkDelete(ctx context.Context, session sqlx.Session, builder sqlbuilder.DeleteBuilder) error{
    query, args := m.deleteBuilder(builder).Build()
	_, err := m.exec(ctx, session, query, args)
	return err
}

func (m *default{{.upperStartCamelObject}}Inter) Goroutines(ctx context.Context,n int) *pool.ContextPool {
	return pool.New().WithMaxGoroutines(int(n/50) + 1).WithContext(ctx)
}